const os = require("os");
const path = require("path");
// ESLint插件
// const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
// 图片压缩
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
//代码压缩
const TerserWebpackPlugin = require("terser-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");
// VueLoader
const VueLoaderPlugin = require("vue-loader/lib/plugin-webpack5");
const { DefinePlugin } = require("webpack");
// 进度条插件
const WebpackBar = require('webpackbar');
// 离线化
// const WorkboxPlugin = require("workbox-webpack-plugin");
// // cpu核数
const threads = os.cpus().length;
// 需要通过 cross-env 定义环境变量
const isProduction = process.env.NODE_ENV === "production";

const getStyleLoaders = (preProcessor) => {
    return [
        isProduction ? MiniCssExtractPlugin.loader : "vue-style-loader",
        "css-loader",
        // Css 兼容性处理
        {
            loader: "postcss-loader",
            options: {
                postcssOptions: {
                    plugins: ["postcss-preset-env"],// 能解决大多数样式兼容性问题
                },
            },
        },
        preProcessor && {
            loader: preProcessor,
            // options:
            //     preProcessor === "sass-loader"
            //         ? {
            //             // 自定义主题：自动引入我们定义的scss文件
            //             additionalData: `@use "@/styles/element/index.scss" as *;`,
            //         }
            //         : {},
        },
    ].filter(Boolean);
};

module.exports = {
    // 入口
    entry: "./src/main.js",
    // 出口
    output: {
        // 出口文件inde.html访问的路径
        publicPath: '/',
        path: isProduction ? path.resolve(__dirname, "dist") : undefined,
        // 普通文件
        filename: isProduction
            ? "js/[name].[contenthash:10].js"
            : "js/[name].js",
        // 异步加载的文件（比如点击执行的）
        chunkFilename: isProduction
            ? "js/[name].[contenthash:10].chunk.js"
            : "js/[name].chunk.js",
        // 静态文件
        assetModuleFilename: "assets/[hash:10][ext][query]",
        clean: true,
        // 默认 webpack 会在输出的 bundle 中生成路径信息，将路径信息删除可小幅提升构建速度。
        pathinfo: false,
    },
    module: {
        // lodaer存放地区
        // 资源模块(asset module)是一种模块类型，它允许使用资源文件（字体，图标等）而无需配置额外 loader。
        // asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
        // asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
        // asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
        // asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader，并且配置资源体积限制实现。
        rules: [
            {
                test: /\.css$/,
                // use 数组里面 Loader 执行顺序是从右到左
                use: getStyleLoaders(),
            },
            {
                test: /\.s[ac]ss$/,
                use: getStyleLoaders("sass-loader"),
            },
            {
                test: /\.(png|jpe?g|gif|svg)$/,
                type: "asset",
                parser: {
                    dataUrlCondition: {
                        maxSize: 10 * 1024, //图片大于10*1024进行base64转码
                    },
                },
                generator: {
                    filename: 'assets/img/[hash][ext][query]' // 局部指定输出位置
                }
            },
            {
                test: /\.(ttf|woff2?|map3|avi|map4|mp4)$/,
                type: "asset/resource",
                generator: {
                    filename: "assets/media/[hash:8][ext][query]",
                },
            },
            // {
            //     test: /\.(ttf|woff2?)$/,
            //     type: "asset/resource",
            //     generator: {
            //         filename: 'assets/font/[hash][ext][query]' // 局部指定输出位置
            //     }
            // },
            {
                test: /\.js$/,
                include: path.resolve(__dirname, "src"),//include查找src文件下内容，exclude用于排除某文件外
                loader: "babel-loader",
                options: {
                    cacheDirectory: true,// 开启babel编译缓存
                    cacheCompression: false,// 缓存文件不要压缩
                    plugins: [
                        // "@babel/plugin-transform-runtime" //减少代码体积, presets中包含了
                    ],
                },
                // use: [
                //     {
                //         loader: "thread-loader", // 开启多进程
                //         options: {
                //             workers: threads, // 数量
                //         },
                //     },
                //     {
                //         loader: "babel-loader",
                //         options: {
                //             cacheDirectory: true, // 开启babel编译缓存
                //             cacheCompression: false,// 缓存文件不要压缩
                //         },
                //     },
                // ],
            },
            // vue-loader不支持oneOf
            {
                test: /\.vue$/,
                loader: "vue-loader", // 内部会给vue文件注入HMR功能代码
                options: {
                    // 开启缓存
                    cacheDirectory: path.resolve(
                        __dirname,
                        "node_modules/.cache/vue-loader"
                    ),
                },
                // 项目大的话可以尝试给这个loader开启单独线程运行提高效率
                // use: [
                //     {
                //         loader: 'vue-loader',
                //         options: {
                //             // 开启缓存
                //             cacheDirectory: path.resolve(
                //                 __dirname,
                //                 "node_modules/.cache/vue-loader"
                //             ),
                //         },
                //       },
                //      {
                //          loader: 'thread-loader',
                //      },
                // ],
            },
        ],
    },
    // 插件存放地区
    plugins: [
        // new ESLintWebpackPlugin({
        // 指定检查文件的根目录
        //   context: path.resolve(__dirname, "../src"),
        //   exclude: "node_modules",
        //   cache: true,
        //   cacheLocation: path.resolve(
        //     __dirname,
        //     "../node_modules/.cache/.eslintcache"
        //   ),
        // }),
        new WebpackBar({
            // color: "#85d", // 默认green，进度条颜色支持HEX
            // basic: true, // 默认true，启用一个简单的日志报告器
            // profile: true, // 默认false，启用探查器。
        }),
        new HtmlWebpackPlugin({
            // 以 public/index.html 为模板创建文件
            // 新的html文件有两个特点：1. 内容和源文件一致 2. 自动引入打包生成的js等资源
            template: path.resolve(__dirname, "public/index.html"),
        }),
        // 复制文件
        new CopyPlugin({
            patterns: [
                {
                    from: path.resolve(__dirname, "public"),
                    to: path.resolve(__dirname, "dist"),
                    toType: "dir",
                    noErrorOnMissing: true,
                    globOptions: {
                        ignore: ["**/index.html"],
                    },
                    info: {
                        minimized: true,
                    },
                },
            ],
        }),
        // 提取css成单独文件
        isProduction &&
        new MiniCssExtractPlugin({
            // 定义输出文件名和目录
            filename: "assets/css/[name].[contenthash:10].css",
            chunkFilename: "assets/css/[name].[contenthash:10].chunk.css",
        }),
        new VueLoaderPlugin(),
        new DefinePlugin({
            __VUE_OPTIONS_API__: "true",
            __VUE_PROD_DEVTOOLS__: "false",
            'process.env': {
                VUE_APP_HOST: isProduction // 将属性转化为全局变量，让代码中可以正常访问
            }
        }),
        // // https和本地可以用
        // new WorkboxPlugin.GenerateSW({
        //     // 这些选项帮助快速启用 ServiceWorkers
        //     // 不允许遗留任何“旧的” ServiceWorkers，更新代码自动替换资源刷新网页
        //     clientsClaim: true,
        //     skipWaiting: true,
        //     exclude: [/.*\.mp4$/], // 此处添加过滤规则
        //     maximumFileSizeToCacheInBytes: 10485760, // 适当调整预缓存的单个文件大小上限10mb以下的存储
        // }),
    ].filter(Boolean),
    // 代码处理
    optimization: {
        // 告知 webpack 使用 TerserPlugin 或其它在 optimization.minimizer定义的插件压缩 bundle
        minimize: isProduction,
        // 压缩的操作
        minimizer: [
            new CssMinimizerPlugin(),//压缩css
            // 当生产模式会默认开启TerserPlugin，压缩 JavaScript，但是我们需要进行其他配置，就要重新写了
            new TerserWebpackPlugin({//代码压缩
                parallel: threads, // 开启多进程,填入数字是开启几个线程
                terserOptions: {
                    compress: {
                        drop_console: true, //去除log
                    },
                },
            }),
            // 压缩图片
            new ImageMinimizerPlugin({
                minimizer: {
                    implementation: ImageMinimizerPlugin.imageminGenerate,
                    options: {
                        plugins: [
                            ["gifsicle", { interlaced: true }],
                            ["jpegtran", { progressive: true }],
                            ["optipng", { optimizationLevel: 5 }],
                            [
                                "svgo",
                                {
                                    plugins: [
                                        "preset-default",
                                        "prefixIds",
                                        {
                                            name: "sortAttrs",
                                            params: {
                                                xmlnsOrder: "alphabetical",
                                            },
                                        },
                                    ],
                                },
                            ],
                        ],
                    },
                },
            }),
        ],
        // 拆包区域
        splitChunks: {
            chunks: "all",  //指定打包同步加载还是异步加载 
            cacheGroups: {
                // layouts通常是admin项目的主体布局组件，所有路由组件都要使用的
                // 可以单独打包，从而复用
                // 如果项目中没有，请删除
                // layouts: {
                //     name: "layouts",
                //     test: path.resolve(__dirname, "../src/layouts"),
                //     priority: 40,
                // },
                // 如果项目中使用element-plus，此时将所有node_modules打包在一起，那么打包输出文件会比较大。
                // 所以我们将node_modules中比较大的模块单独打包，从而并行加载速度更好
                // 如果项目中没有，请删除
                elementUI: {
                    name: "chunk-element",
                    test: /[\\/]node_modules[\\/]_?element-ui(.*)/,
                    priority: 30,
                },
                // 将vue相关的库单独打包，减少node_modules的chunk体积。
                vue: {
                    name: "vue",
                    test: /[\\/]node_modules[\\/]vue(.*)[\\/]/,
                    chunks: "initial",
                    priority: 20,
                },
                libs: {
                    name: "chunk-libs",
                    test: /[\\/]node_modules[\\/]/,
                    priority: 10, // 权重最低，优先考虑前面内容
                    chunks: "initial",
                },
            },
        },
        // 为运行时代码创建一个额外的 chunk，减少 entry chunk 体积，提高性能。
        runtimeChunk: {
            name: (entrypoint) => `runtime~${entrypoint.name}`,
        },
    },
    // resolve 用来配置 webpack 如何解析模块，可通过优化 resolve 配置来覆盖默认配置项，减少解析范围
    resolve: {
        modules: ['node_modules'],// 指定查找依赖包目录
        // 需要解析的文件类型列表。由于 webpack 的解析顺序是从左到右，因此要将使用频率高的文件类型放在左侧，如下我将 vue 放在最左侧。
        extensions: [".vue", ".js", ".json"],
        alias: {
            // 路径别名
            "@": path.resolve(__dirname, "src"),
        },
        fallback: { "stream": false } //vue-loader相关报错解决办法
    },
    // 开发服务器
    devServer: {
        open: true,
        host: "localhost",
        port: 3000,
        hot: true,  //热模块更新
        compress: true,
        historyApiFallback: true, // 解决vue-router刷新404问题
    },
    //Webpack默认在生产环境下（mode:'production'）自动进行代码压缩，内部用的是terser-webpack-plugin插件
    mode: isProduction ? "production" : "development",
    //错误代码信息标注，第一个是行，第二个是行加列，关闭默认按最大性能处理，开启方便查错
    // devtool: isProduction ? "source-map" : "cheap-module-source-map",
    performance: false,
};
// Tree Shaking 也叫摇树优化，基于ESModules模块化（即只有ESModules的模块化代码才能使Tree Shaking生效），在production生产环境下默认开启
